#!/usr/bin/env python3
"""Shell interface for pipeline kcidb data."""
import argparse
import json
import sys
import typing

from cki_lib import misc
from cki_lib.kcidb.file import KCIDBFile
import sentry_sdk

# shortcuts as e.g. 'misc-tag' -> 'misc/tag'
NESTED_KEYS: typing.Dict[str, str] = {}


def parse_time(value):
    """For value==now, return utc_now_iso(), otherwise raise()."""
    if value != 'now':
        raise Exception('Only "now" is supported as time value')
    return misc.utc_now_iso()


def _get(kcidb_file, args):
    getter = getattr(kcidb_file, f'get_{args.toplevel_object}')
    return getter(args.id)


def kcidb_set(kcidb_file, args):
    """Set key=value in toplevel_object."""
    if args.key in NESTED_KEYS:
        args.key = NESTED_KEYS[args.key]

    obj = _get(kcidb_file, args)
    misc.set_nested_key(obj, args.key, args.value)
    kcidb_file.save()


def kcidb_append(kcidb_file, args):
    """Append value in key in toplevel_object."""
    if args.key in NESTED_KEYS:
        args.key = NESTED_KEYS[args.key]

    obj = _get(kcidb_file, args)
    misc.append_to_nested_list(obj, args.key, args.value)
    kcidb_file.save()


def kcidb_append_json(kcidb_file, args):
    """Append json object in key in toplevel_object."""
    obj = _get(kcidb_file, args)
    misc.append_to_nested_list(obj, args.key, json.loads(args.value))
    kcidb_file.save()


def kcidb_get(kcidb_file, args):
    """Print value for key from toplevel_object."""
    if args.key in NESTED_KEYS:
        args.key = NESTED_KEYS[args.key]

    obj = _get(kcidb_file, args)
    value = misc.get_nested_key(obj, args.key)

    if args.json:
        value = json.dumps(value)

    print(value)


def create_checkout(kcidb_file, args):
    """Create checkout."""
    kcidb_file.set_checkout(
        args.id,
        {
            'id': args.id,
            'origin': 'redhat',
        }
    )
    kcidb_file.save()


def create_build(kcidb_file, args):
    """Create build."""
    kcidb_file.set_build(
        args.id,
        {
            'id': args.id,
            'origin': 'redhat',
            'checkout_id': args.parent_id,
        }
    )
    kcidb_file.save()


def create_test(kcidb_file, args):
    """Create test."""
    kcidb_file.set_test(
        args.id,
        {
            'id': args.id,
            'origin': 'redhat',
            'build_id': args.parent_id,
        }
    )
    kcidb_file.save()


def create(kcidb_file, args):
    """Create checkout or build."""
    if args.toplevel_object == 'checkout':
        create_checkout(kcidb_file, args)
    elif args.toplevel_object == 'build':
        create_build(kcidb_file, args)
    elif args.toplevel_object == 'test':
        create_test(kcidb_file, args)
    else:
        raise NotImplementedError(f'{args.toplevel_object} not handled')


def parse_args(argv):
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(
        description='Parse a KCIDB file. Allows getting and setting values.'
    )

    parser.add_argument(
        'kcidb_file',
        type=str,
        help='A path to the KCIDB file to read/write.',
    )

    parser.add_argument(
        'toplevel_object',
        choices=['checkout', 'build', 'test'],
        help='Which toplevel KCIDB object to operate on.',
    )
    parser.add_argument('id', type=str, help='KCIDB object id')

    subparsers = parser.add_subparsers(dest='action')
    subparsers.required = True  # py<3.7 compat

    # Get parser
    parser_get = subparsers.add_parser('get')
    parser_get.set_defaults(func=kcidb_get)
    parser_get.add_argument('key', type=str)
    parser_get.add_argument('--json', action='store_true',
                            help='Dump output json encoded')

    # Set parsers
    setters = [
        ('', str),
        ('-bool', misc.strtobool),
        ('-json', json.loads),
        ('-int', int),
        ('-time', parse_time),
    ]

    for name, value_type in setters:
        subparser = subparsers.add_parser(f'set{name}')
        subparser.set_defaults(func=kcidb_set)
        subparser.add_argument('key', type=str)
        subparser.add_argument('value', type=value_type)

    parser_create = subparsers.add_parser('create')
    parser_create.set_defaults(func=create)
    parser_create.add_argument(
        'parent_id',
        type=str,
        nargs='?',
        default=None,
        help='ID of parent KCIDB object. checkout_id for builds, build_id for tests.'
    )

    parser_append_dict = subparsers.add_parser('append-dict')
    parser_append_dict.set_defaults(func=kcidb_append)
    parser_append_dict.add_argument('key', type=str)
    parser_append_dict.add_argument('value', action=misc.StoreNameValuePair,
                                    help="Parse key=value into a dictionary",
                                    nargs='+',
                                    metavar="KEY=VALUE")

    parser_append_json = subparsers.add_parser('append-json')
    parser_append_json.set_defaults(func=kcidb_append_json)
    parser_append_json.add_argument('key', type=str)
    parser_append_json.add_argument('value', type=str)

    return parser.parse_args(argv)


def main(argv):
    """Open file and run appropriate function."""
    args = parse_args(argv)

    kcidb_file = KCIDBFile(args.kcidb_file)

    args.func(kcidb_file, args)


if __name__ == '__main__':
    misc.sentry_init(sentry_sdk)
    main(sys.argv[1:])
